﻿using Newtonsoft.Json.Linq;
using StbImageSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using ttlib;
using static System.Net.Mime.MediaTypeNames;

namespace TexPacker
{
    class Image
    {
        public byte[] datargba;

        public int Width
        {
            get;
            private set;
        }
        public int Height
        {
            get;
            private set;
        }
        public byte GetGray(int x, int y)
        {
            var seek = (y * Width + x) * 4;
            var gray = datargba[seek + 0] * 0.3 + datargba[seek + 1] * 0.6 + datargba[seek + 2] * 0.1;

            return (byte)gray;
        }
        public Image(ImageResult image)
        {
            this.Width = image.Width;
            this.Height = image.Height;


            int srcbytelen = 4;
            if (image.Comp == ColorComponents.RedGreenBlueAlpha)
            {
                srcbytelen = 4;
            }
            else if (image.Comp == ColorComponents.RedGreenBlue)
            {
                srcbytelen = 3;

            }
            else if (image.Comp == ColorComponents.Grey)
            {
                srcbytelen = 1;
            }
            else
            {
                throw new Exception("error format.");
            }



            this.datargba = new byte[this.Width * this.Height * 4];
            for (var y = 0; y < Height; y++)
            {
                for (var x = 0; x < Width; x++)
                {
                    var seek = (y * Width + x) * srcbytelen;
                    var destseek = (y * Width + x) * 4;
                    if (srcbytelen >= 3)
                    {
                        this.datargba[destseek + 0] = image.Data[seek + 0];
                        this.datargba[destseek + 1] = image.Data[seek + 1];
                        this.datargba[destseek + 2] = image.Data[seek + 2];
                    }
                    if (srcbytelen == 1)
                    {
                        this.datargba[destseek + 0] = image.Data[seek + 0];
                        this.datargba[destseek + 1] = image.Data[seek + 0];
                        this.datargba[destseek + 2] = image.Data[seek + 0];
                    }
                    if (srcbytelen == 4)
                    {
                        this.datargba[destseek + 3] = image.Data[seek + 3];
                    }
                    else
                    {
                        this.datargba[destseek + 3] = 255;
                    }
                }
            }

        }
        public Image(int width, int height, byte[] rgbadata)
        {
            this.Width = width;
            this.Height = height;
            this.datargba = new byte[width * height * 4];
            for (var i = 0; i < datargba.Length; i++)
            {
                datargba[i] = rgbadata[i];
            }

        }
        public Image(int width, int height)
        {
            this.Width = width;
            this.Height = height;
            this.datargba = new byte[width * height * 4];
        }
        public void SetImageData(byte[] imagedata, int x, int y, int w, int h)
        {
            for (var _iy = 0; _iy < h; _iy++)
            {
                for (var _ix = 0; _ix < w; _ix++)
                {
                    var sseek = (_iy * w + _ix) * 4;
                    var dseek = ((_iy + y) * this.Width + (_ix + x)) * 4;
                    datargba[dseek + 0] = imagedata[sseek + 0];
                    datargba[dseek + 1] = imagedata[sseek + 1];
                    datargba[dseek + 2] = imagedata[sseek + 2];
                    datargba[dseek + 3] = imagedata[sseek + 3];
                }
            }
        }
        public Rectangle UpdateRange()
        {
            int x1 = int.MaxValue;
            int x2 = int.MinValue;
            int y1 = int.MaxValue;
            int y2 = int.MinValue;
            for (var y = 0; y < Height; y++)
            {
                for (var x = 0; x < Width; x++)
                {
                    var seek = (y * Width + x) * 4;

                    {
                        int alpha = (int)((this.datargba[seek + 3] / 255.0) * 7);
                        if (alpha > 0)
                        {
                            if (x1 > x) x1 = x;
                            if (x2 < x) x2 = x;
                            if (y1 > y) y1 = y;
                            if (y2 < y) y2 = y;
                        }
                    }
                }
            }
            return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
        }
        public byte[] SubImageData(int x, int y, int w, int h)
        {
            var rgbadata = new byte[w * h * 4];
            for (var _iy = 0; _iy < h; _iy++)
            {
                for (var _ix = 0; _ix < w; _ix++)
                {
                    var dseek = (_iy * w + _ix) * 4;
                    var sseek = ((_iy + y) * this.Width + (_ix + x)) * 4;
                    rgbadata[dseek + 0] = this.datargba[sseek + 0];
                    rgbadata[dseek + 1] = this.datargba[sseek + 1];
                    rgbadata[dseek + 2] = this.datargba[sseek + 2];
                    rgbadata[dseek + 3] = this.datargba[sseek + 3];
                }
            }
            return rgbadata;
        }
        public CutImage SubImage(int x, int y, int w, int h)
        {
            var rgbadata = SubImageData(x, y, w, h);
            return new CutImage(w, h, rgbadata);
        }

        public void Save(string filename)
        {
            var imgout = new StbImageWriteSharp.ImageWriter();
            using (var fs = System.IO.File.OpenWrite(filename))
            {
                imgout.WritePng(datargba,Width, Height,StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha, fs);
            }
        }
    }
    class CutImage
    {
        public byte[] datargba;
        public Rectangle range;
        public int srcwidth;
        public int srcheight;
        public int Width
        {
            get
            {
                return range.Width;
            }
        }
        public int Height
        {
            get
            {
                return range.Height;
            }
        }
        public byte GetGray(int x, int y)
        {
            var seek = (y * range.Width + x) * 4;
            var gray = datargba[seek + 0] * 0.3 + datargba[seek + 1] * 0.6 + datargba[seek + 2] * 0.1;

            return (byte)gray;
        }
        public CutImage(ImageResult image)
        {
            this.srcwidth = image.Width;
            this.srcheight = image.Height;
            int x1 = int.MaxValue;
            int x2 = int.MinValue;
            int y1 = int.MaxValue;
            int y2 = int.MinValue;

            int srcbytelen = 4;
            if (image.Comp == ColorComponents.RedGreenBlueAlpha)
            {
                srcbytelen = 4;
            }
            else if (image.Comp == ColorComponents.RedGreenBlue)
            {
                srcbytelen = 3;

            }
            else if (image.Comp == ColorComponents.Grey)
            {
                srcbytelen = 1;
            }
            else
            {
                throw new Exception("error format.");
            }


            for (var y = 0; y < srcheight; y++)
            {
                for (var x = 0; x < srcwidth; x++)
                {
                    var seek = (y * srcwidth + x) * srcbytelen;
                    if (srcbytelen == 4)
                    {
                        int alpha = (int)((image.Data[seek + 3] / 255.0) * 7);
                        if (alpha > 0)
                        {
                            if (x1 > x) x1 = x;
                            if (x2 < x) x2 = x;
                            if (y1 > y) y1 = y;
                            if (y2 < y) y2 = y;
                        }
                    }
                    else
                    {
                        x1 = 0;
                        x2 = srcwidth - 1;
                        y1 = 0;
                        y2 = srcheight - 1;

                        break;
                    }
                }
            }

            this.range.X = x1;
            this.range.Y = y1;
            this.range.Width = x2 - x1 + 1;
            this.range.Height = y2 - y1 + 1;

            this.datargba = new byte[this.Width * this.Height * 4];
            for (var y = y1; y <= y2; y++)
            {
                for (var x = x1; x <= x2; x++)
                {
                    var seek = (y * srcwidth + x) * srcbytelen;
                    var destseek = ((y - y1) * this.range.Width + (x - x1)) * 4;
                    if (srcbytelen >= 3)
                    {
                        this.datargba[destseek + 0] = image.Data[seek + 0];
                        this.datargba[destseek + 1] = image.Data[seek + 1];
                        this.datargba[destseek + 2] = image.Data[seek + 2];
                    }
                    if (srcbytelen == 1)
                    {
                        this.datargba[destseek + 0] = image.Data[seek + 0];
                        this.datargba[destseek + 1] = image.Data[seek + 0];
                        this.datargba[destseek + 2] = image.Data[seek + 0];
                    }
                    if (srcbytelen == 4)
                    {
                        this.datargba[destseek + 3] = image.Data[seek + 3];
                    }
                    else
                    {
                        this.datargba[destseek + 3] = 255;
                    }
                }
            }

        }
        public CutImage(int width, int height, byte[] rgbadata)
        {
            this.srcwidth = width;
            this.srcheight = height;

            int x1 = int.MaxValue;
            int x2 = int.MinValue;
            int y1 = int.MaxValue;
            int y2 = int.MinValue;
            for (var y = 0; y < srcheight; y++)
            {
                for (var x = 0; x < srcwidth; x++)
                {
                    var seek = (y * srcwidth + x) * 4;

                    {
                        int alpha = (int)((rgbadata[seek + 3] / 255.0) * 7);
                        if (alpha > 0)
                        {
                            if (x1 > x) x1 = x;
                            if (x2 < x) x2 = x;
                            if (y1 > y) y1 = y;
                            if (y2 < y) y2 = y;
                        }
                    }

                }
            }

            this.range.X = x1;
            this.range.Y = y1;
            this.range.Width = x2 - x1 + 1;
            this.range.Height = y2 - y1 + 1;

            this.datargba = new byte[this.Width * this.Height * 4];
            for (var y = y1; y <= y2; y++)
            {
                for (var x = x1; x <= x2; x++)
                {
                    var seek = (y * srcwidth + x) * 4;
                    var destseek = ((y - y1) * this.range.Width + (x - x1)) * 4;

                    {
                        this.datargba[destseek + 0] = rgbadata[seek + 0];
                        this.datargba[destseek + 1] = rgbadata[seek + 1];
                        this.datargba[destseek + 2] = rgbadata[seek + 2];
                        this.datargba[destseek + 3] = rgbadata[seek + 3];
                    }

                }
            }
        }
        public byte[] SubImageData(int x, int y, int w, int h)
        {
            var rgbadata = new byte[w * h * 4];
            for (var _iy = 0; _iy < h; _iy++)
            {
                for (var _ix = 0; _ix < w; _ix++)
                {
                    var dseek = (_iy * w + _ix) * 4;
                    var sseek = ((_iy + y) * this.Width + (_ix + x)) * 4;
                    rgbadata[dseek + 0] = this.datargba[sseek + 0];
                    rgbadata[dseek + 1] = this.datargba[sseek + 1];
                    rgbadata[dseek + 2] = this.datargba[sseek + 2];
                    rgbadata[dseek + 3] = this.datargba[sseek + 3];
                }
            }
            return rgbadata;
        }
        public CutImage SubImage(int x, int y, int w, int h)
        {
            var rgbadata = SubImageData(x, y, w, h);
            return new CutImage(w, h, rgbadata);
        }
    }

    class OutPackImage
    {
        byte[] data;
        bool gray;
        int width;
        int height;
        List<ttlib.SpriteInfo> sprites = new List<ttlib.SpriteInfo>();
        public OutPackImage(int width, int height, bool gray = false)
        {
            this.gray = gray;
            this.width = width;
            this.height = height;
            this.data = new byte[width * height * (gray ? 1 : 4)];
        }
        public void SetImage(string name, PicOption? option, CutImage srcimage, int x, int y)
        {
            var s = new SpriteInfo();
            s.name = name;
            s.uvx = (uint)x;
            s.uvy = (uint)y;
            s.uvwidth = (uint)srcimage.Width;
            s.uvheight = (uint)srcimage.Height;

            if (option != null)
            {
                s.option = option.Value;
            }

            sprites.Add(s);
            int bytelen = gray ? 1 : 4;
            for (var j = 0; j < srcimage.Height; j++)
            {
                for (var i = 0; i < srcimage.Width; i++)
                {
                    var destseek = ((j + y) * width + (i + x)) * bytelen;
                    if (gray)
                    {
                        data[destseek + 0] = srcimage.GetGray(i, j);
                    }
                    else
                    {
                        var srcseek = (j * srcimage.Width + i) * 4;
                        {
                            data[destseek + 0] = srcimage.datargba[srcseek + 0];
                            data[destseek + 1] = srcimage.datargba[srcseek + 1];
                            data[destseek + 2] = srcimage.datargba[srcseek + 2];
                            data[destseek + 3] = srcimage.datargba[srcseek + 3];
                        }
                    }
                }
            }
        }
        public void Save(string filename)
        {
            var imgout = new StbImageWriteSharp.ImageWriter();
            using (var fs = System.IO.File.OpenWrite(filename))
            {
                imgout.WritePng(data, width, height, gray ? StbImageWriteSharp.ColorComponents.Grey : StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha, fs);
            }

            {
                var jsonpath = filename + ".json";
                JObject json = new JObject();
                json["tex"] = System.IO.Path.GetFileName(filename);
                json["texsize"] = this.width + "," + this.height;
                JArray sprites = new JArray();
                json["sprites"] = sprites;

                foreach (var f in this.sprites)
                {
                    var spriteItem = new JObject();
                    spriteItem["name"] = f.name;
                    spriteItem["uv"] = f.uvx + "," + f.uvy + "," + f.uvwidth + "," + f.uvheight;
                    spriteItem["border"] = f.option.borderLeft + "," + f.option.borderTop + "," + f.option.borderRight + "," + f.option.borderBottom;
                    //spriteItem["pivot"] =f.option.pivotX+","+f.option.pivotY;

                    sprites.Add(spriteItem);
                }
                System.IO.File.WriteAllText(jsonpath, json.ToString());
            }
        }

    }
}
